/*
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


/** Lexical scanner for Open Shading Language
 **/

/************************************************************
 * Definitions section
 ************************************************************/


/* Option 'noyywrap' indicates that when EOF is hit, yyin does not 
 * automatically reset to another file.
 */
%option noyywrap

 /* Option 'prefix' creates a C++ lexer with the given prefix, so that
  * we can link with other flex-generated lexers in the same application
  * without name conflicts.
  */
%option prefix="osl"


 /* Define regular expression macros 
  ************************************************/

 /* white space, not counting newline */
WHITE           [ \t\v\f\r]+
 /* alpha character */
ALPHA           [A-Za-z]
 /* numerals */
DIGIT           [0-9]
 /* Integer literal */
INTEGER         {DIGIT}+
 /* floating point literal (E, FLT1, FLT2, FLT3 are just helpers)
  * NB: we don't allow leading +/- due to ambiguity between 
  * whether "a-0.5" is really "a -0.5" or "a - 0.5".  Resolve this
  * in the grammar.
  */
E               [eE][-+]?{DIGIT}+
FLT1            {DIGIT}+\.{DIGIT}*{E}?
FLT2            {DIGIT}*\.{DIGIT}+{E}?
FLT3            {DIGIT}+{E}
FLT             {FLT1}|{FLT2}|{FLT3}
 /* string literal */
STR     \"(\\.|[^\\"])*\"
STRDUMMY \"(\\.|[^\\"])*\"
 /* Dumb note: STRDUMMY is just to fix my syntax highlighting in emacs,
  * which is thrown off by the odd number of quotes!
  */
 /* Identifier: alphanumeric, may contain digits after the first character */
IDENT           ({ALPHA}|[_])({ALPHA}|{DIGIT}|[_])*
 /* C preprocessor (cpp) directives */
CPP             ^[ \t]*#.*\n
CPLUSCOMMENT    \/\/.*\n



 /* Note for lex newbies: the following '%{ .. %}' section contains literal
  * C code that will be inserted at the top of code that flex generates.
  */
%{
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <string>

#include "oslcomp_pvt.h"
using namespace OSL;
using namespace OSL::pvt;

#include "oslgram.hpp"   /* Generated by bison/yacc */

#define yylval osllval
#define yylloc osllloc

void preprocess (const char *yytext);

// Macro that sets the yylloc line variables to the current parse line.
#define SETLINE yylloc.first_line = yylloc.last_line = oslcompiler->lineno()

%}


%%

 /************************************************
  * Lexical matching rules
  ************************************************/

 /* preprocessor symbols */
{CPP}	 	        {  preprocess (YYText()); SETLINE; }

 /* Comments */
{CPLUSCOMMENT}          {  oslcompiler->incr_lineno(); /* skip it */
                           SETLINE;
                        }

 /* keywords */
"break"			{  SETLINE;  return (yylval.i=BREAK); }
"closure"		{  SETLINE;  return (yylval.i=CLOSURE); }
"color"			{  SETLINE;  return (yylval.i=COLORTYPE); }
"continue"		{  SETLINE;  return (yylval.i=CONTINUE); }
"do"		        {  SETLINE;  return (yylval.i=DO); }
"else"			{  SETLINE;  return (yylval.i=ELSE); }
"float"			{  SETLINE;  return (yylval.i=FLOATTYPE); }
"for"			{  SETLINE;  return (yylval.i=FOR); }
"if"			{  SETLINE;  return (yylval.i=IF); }
"illuminance"	        {  SETLINE;  return (yylval.i=ILLUMINANCE); }
"illuminate"	        {  SETLINE;  return (yylval.i=ILLUMINATE); }
"int"		        {  SETLINE;  return (yylval.i=INTTYPE); }
"matrix"		{  SETLINE;  return (yylval.i=MATRIXTYPE); }
"normal"		{  SETLINE;  return (yylval.i=NORMALTYPE); }
"output"                {  SETLINE;  return (yylval.i=OUTPUT); }
"point"			{  SETLINE;  return (yylval.i=POINTTYPE); }
"public"		{  SETLINE;  return (yylval.i=PUBLIC); }
"return"		{  SETLINE;  return (yylval.i=RETURN); }
"string"		{  SETLINE;  return (yylval.i=STRINGTYPE); }
"struct"		{  SETLINE;  return (yylval.i=STRUCT); }
"vector"		{  SETLINE;  return (yylval.i=VECTORTYPE); }
"void"			{  SETLINE;  return (yylval.i=VOIDTYPE); }
"while"			{  SETLINE;  return (yylval.i=WHILE); }

 /* reserved words */
"bool"|"case"|"char"|"class"|"const"|"default"|"double" |    \
"enum"|"extern"|"false"|"friend"|"inline"|"long"|"private" | \
"protected"|"short"|"signed"|"sizeof"|"static"|"struct" |    \
"switch"|"template"|"this"|"true"|"typedef"|"uniform" |      \
"union"|"unsigned"|"varying"|"virtual" {
                            fprintf (stderr, "Error: \"%s\", line %d:\n"
                                     "\t'%s' is a reserved word\n",
                                     oslcompiler->filename().c_str(),
                                     oslcompiler->lineno(), YYText());
                            SETLINE;
                            return (yylval.i=RESERVED);
                        }


 /* Identifiers */
{IDENT}	                {
                            yylval.s = ustring(YYText()).c_str();
                            SETLINE;
                            return IDENTIFIER;
                        }

 /* Literal values */
{INTEGER}               {
                            yylval.i = atoi (YYText());
                            SETLINE;
                            return INT_LITERAL;
                        }

{FLT}                   {
                            yylval.f = atof (YYText());
                            SETLINE;
                            return FLOAT_LITERAL;
                        }

{STR}                   {
                            // grab the material between the quotes
                            ustring s (YYText(), 1, strlen(YYText())-2);
                            yylval.s = s.c_str();
                            SETLINE;
                            return STRING_LITERAL;
                        }

 /* The one-char operators (like "+") will return correctly with the
  * catch-all rule, but we need to define the two-character operators
  * so they are not lexed as '+' and '=' separately, for example.
  */
"+="			{  SETLINE;  return (yylval.i=ADD_ASSIGN); }
"-="			{  SETLINE;  return (yylval.i=SUB_ASSIGN); }
"*="			{  SETLINE;  return (yylval.i=MUL_ASSIGN); }
"/="			{  SETLINE;  return (yylval.i=DIV_ASSIGN); }
"&="			{  SETLINE;  return (yylval.i=BIT_AND_ASSIGN); }
"|="			{  SETLINE;  return (yylval.i=BIT_OR_ASSIGN); }
"^="			{  SETLINE;  return (yylval.i=XOR_ASSIGN); }
"<<="			{  SETLINE;  return (yylval.i=SHL_ASSIGN); }
">>="			{  SETLINE;  return (yylval.i=SHR_ASSIGN); }
"<<"			{  SETLINE;  return (yylval.i=SHL_OP); }
">>"			{  SETLINE;  return (yylval.i=SHR_OP); }
"&&"			{  SETLINE;  return (yylval.i=AND_OP); }
"||"			{  SETLINE;  return (yylval.i=OR_OP); }
"<="			{  SETLINE;  return (yylval.i=LE_OP); }
">="			{  SETLINE;  return (yylval.i=GE_OP); }
"=="			{  SETLINE;  return (yylval.i=EQ_OP); }
"!="			{  SETLINE;  return (yylval.i=NE_OP); }
"++"                    {  SETLINE;  return (yylval.i=INCREMENT); }
"--"                    {  SETLINE;  return (yylval.i=DECREMENT); }

 /* Beginning of metadata */
"[["                    {  SETLINE;  return (yylval.i=METADATA_BEGIN); }

 /* End of line */
"\\\n"			|
[\n]			{  oslcompiler->incr_lineno();
                           SETLINE;
                        }

 /* Ignore whitespace */
{WHITE} 		{  }

 /* catch-all rule for any other single characters */
.			{  SETLINE;  return (yylval.i = *YYText()); }

%%


void
preprocess (const char *yytext)
{
#if 0
    printf ("preprocess: <%s>\n", yytext);
#endif
    const char *p = yytext;
    while (*p == ' ' || *p == '\t')
        p++;
    if (*p != '#') {
	fprintf (stderr, "Possible bug in shader preprocess\n");
        SETLINE;
	return;
    }
    p++;
    while (*p == ' ' || *p == '\t')
        p++;
    if (! strncmp (p, "pragma", 6)) {
	// pragma
	fprintf (stderr, "Unknown pragma '%s'\n", p);
        oslcompiler->incr_lineno();  // the pragma ends with an EOLN
    } else {  /* probably the line number and filename */
        if (! strncmp (p, "line", 4))
            p += 4;
        int line = atoi (p);
        if (line > 0) {
            oslcompiler->lineno (line);
	    const char *f = strchr (yytext, '\"');
	    if (f) {
                ++f;  // increment to past the quote
	        int len = 0;  // count of chars within quotes
	        while (f[len] && f[len] != '\"')
		    ++len;
	        oslcompiler->filename (ustring (f, len));
            }
	} else {
            fprintf (stderr, "Error: \"%s\", line %d:\n"
                     "\tUnrecognized preprocessor command: #%s\n",
                     oslcompiler->filename().c_str(), oslcompiler->lineno(), p);
        }
    }
    SETLINE;
}
